perm filename FREEFO.LES[UP,DOC]3 blob sn#056649 filedate 1973-08-03 generic text, type T, neo UTF8
COMMENT ⊗   VALID 00003 PAGES 
RECORD PAGE   DESCRIPTION
 00001 00001
 00002 00002	BEGIN  "FREEFOROL"
 00008 00003	DEFINE NOVA="25"  COMMENT MAXIMUM NUMBER OF STRING SUBSTITUTIONS
 00013 ENDMK
⊗;
BEGIN  "FREEFOROL"
	COMMENT  10 MARCH 1973

OPERATION

Freeforol is a text macro processor that can be used to generate form
letters  and other fill-in-the-blanks text.  If you say  R FREEFO, it
types a "*" and expects a keyboard input of the form
    <source file list>
or
    <destination file>←<source file list>
where the  <source  file list>  consists of  one or  more file  names
separated  by  commas.    These  must  be  text  files  and  will  be
effectively concatenated in the order given.

The output goes to the destination file,  if  given.   Otherwise  one
copy of the output is spooled.

PROCESS

The source file(s) are expected to contain a text macro  followed  by
one or more argument lists.  This program outputs a copy of the macro
for each arguments list, with arguments substituted.

The first  character  of the  source file  (neglecting any  directory
page  or line numbers)  is taken as  the macro delimiter.   The macro
begins just  after this.    Each place  where an  argument is  to  be
substituted  is designated  by a  <delimiter><parameter> pair,  where
the  <parameter>  may  be "1"  through  "9"  (representing  the first
through  ninth arguments)  or  "A"  through "J"  (10th  through  20th
arguments).  The macro ends with
    <delimiter><separator><delimeter>
where the <separator> is any non-<parameter>.

The argument lists begin on the next line, which can of course be  in
a  different file if you wish.  Either of two formats may be used, as
follows.

1) If the <separator> is <CR> or <CR><LF>, then each line of text  is
interpreted  as  an  argument  and  the list is terminated by a blank
line.   This format is convenient for lists of addressees that are to
be inserted in a form letter.

2) If the separator is any other character, then each line of text is
interpreted  as  a  list  whose  arguments  are  separated   by   the
<separator>.

Thus, the general form of the source file(s) is
	<delimiter>
	<macro body, including parameters>
	<delimiter><separator><delimiter>
	<argument lists>

EXAMPLES

For example, suppose the following source file were used:
_____________________________________________________________________
⊗		      Veterans Administration
                          Washington, D. C.
						          4 July 1984
⊗1 ⊗2
⊗3
⊗4

Dear ⊗1:

Our office has  recently  determined  that  you  qualify  for  a  10%
disability.   Congratulations ...

⊗
⊗
John
Brown
Arlington National Cemetery
Arlington, Virginia

Speedy
Gonzales
Blue Fox Restaurant
Tijuana, Mexico

_____________________________________________________________________

Then "⊗" is the delimiter and the separator  is  <CR><LF>,  so  there
would  be  two  copies  of  the  letter generated, with the first one
addressed:
John Brown
Arlington National Cemetery
Arlington, Virginia

Dear John:
....

Alternatively, if the source file were:
_____________________________________________________________________
@
	This is a@2 macro processor,
	but it is @1.

@,@
fairly general, simple
rather easy to understand,n elegant
_____________________________________________________________________
then "@" is the delimiter and "," is the separator, so this produces:
	This is a simple macro processor,
	but it is fairly general.

	This is an elegant macro processor,
	but it is fairly easy to understand.

FEATURES

Several features should be noted.
1)  Any form feeds in the macro body are preserved, but they are
    ignored in the argument lists.
2)  Excess or unused arguments are ignored, so you can put comments
    in the argument lists that will not show up in the output.
3)  Arguments that are omitted are automatically set to NULL.
;
DEFINE NOVA="25";  COMMENT MAXIMUM NUMBER OF STRING SUBSTITUTIONS;
DEFINE ARGS="20";  COMMENT THE MAXIMUM NUMBER OF AGRUMENTS;

DEFINE THIS="COMMENT", CR="'15", LF="'12", HT="'11", FF="'14",↓="'15&'12",
	THRU="STEP 1 UNTIL", ⊃="←1 STEP 1 UNTIL", LN="LENGTH",
	ERR(MES)="BEGIN OUTSTR(""MES""&↓); CALL(0,""EXIT"") END";
DEFINE INCH="1", OUCH="2", LINE="1", MARK="2", CHAR="3",AMP="4",FORMER="5";

REQUIRE 2000 STRING_SPACE;
STRING ARRAY TEXT[1:NOVA];  THIS HOLDS SEGMENTS OF MACRO TEXT;
INTEGER ARRAY PARG[1:NOVA];
STRING ARRAY VAL[1:ARGS];  THIS HOLDS CURRENT ARGUMENT VALUES;
STRING S,S0,SOURCE,DEST;
INTEGER BRK, EOF,T, V, I,FLAG;

EXTERNAL PROCEDURE SPOOL(STRING FILE; INTEGER IOCHAN,FLAGS);

PROCEDURE LOOK;  BEGIN  BOOLEAN FL;  STRING FILE;
	FILE←SCAN(SOURCE,AMP,BRK);
	IF BRK="[" THEN FILE←FILE&"["&SCAN(SOURCE,AMP,BRK)&","&SCAN(SOURCE,AMP,BRK);
	LOOKUP(INCH,FILE,FL);
	IF FL THEN BEGIN OUTSTR(FILE&" not found"&↓); CALL(0,"EXIT") END;
	END;

STRING SIMPLE PROCEDURE READ;	BEGIN  THIS READS THE SOURCE FILE(S);
	STRING RS,SS;	RS←INPUT(INCH,CHAR);
	IF ¬EOF ∨ LN(SOURCE)=0 THEN RETURN(RS);
	CLOSE(INCH);  LOOK;  SS←INPUT(INCH,CHAR);
	IF EQU(SS[1 TO 9],"COMMENT ⊗") THEN BEGIN "TV FILE"
		DO SS←INPUT(INCH,FORMER) UNTIL BRK=FF ∨ EOF;
		SS←INPUT(INCH,CHAR);
		END;
	RETURN(RS&SS)
	END;

	THIS INITIALIZES I/O;
OUTSTR("*");
OPEN(INCH,"DSK",1,2,0,4000,BRK,EOF);  OPEN(OUCH,"DSK",1,0,2,0,BRK,EOF);
SETBREAK(AMP,"←",NULL,"INS");	SETBREAK(FORMER,FF,NULL,"INS");
DEST←SCAN(SOURCE←INCHWL,AMP,BRK);	THIS READS THE FILE NAMES;
IF BRK="←" THEN BEGIN "destination file"
	ENTER(OUCH,DEST,FLAG);	DEST←NULL;
	END
    ELSE BEGIN "spool it"
	SOURCE←DEST; THIS RESTORES SOURCE FILE NAMES;
	DEST←"FREEF";  I←"0"-1;
	DO BEGIN LOOKUP(OUCH,DEST&(I←I+1)&".TMP",FLAG);  CLOSE(OUCH) END
	    UNTIL FLAG;
	ENTER(OUCH,DEST←DEST&I&".TMP",FLAG);
	END;
IF FLAG THEN ERR(Destination file cannot be written);
SETBREAK(AMP,"[,"," 	","INS");  LOOK;

	THIS READS AND SEGMENTS THE TEXT MACRO;
SETBREAK(CHAR,NULL,NULL,"XNA");
IF (I←READ)="C" THEN BEGIN  "FLUSH DIRECTORY"
	DO I←INPUT(INCH,FORMER) UNTIL BRK=FF ∨ EOF;
	I←READ;
	END;
SETBREAK(CHAR,I,NULL,"INS");
S0←READ;  T←0;
DO V←LOP(TEXT[T←T+1]←READ) UNTIL (I←PARG[T]←V-(IF "0"<V≤"9" THEN "0"
    ELSE IF "A"≤V THEN "A"-10 ELSE "A"))≤0 ∨ I>ARGS;

	THIS FINDS THE SEPARATORS AND TERMINATOR;
SETBREAK(MARK,V,NULL,"INS");  SETBREAK(CHAR,LF,CR&FF,"INS");
I←READ;		THIS FLUSHES THE REST OF THE LINE;

DO BEGIN	THIS SCANS ARGUMENT LISTS AND OUTPUTS TEXT;
	IF V=CR THEN BEGIN "1 ARGUMENT PER LINE"
		FOR I ⊃ ARGS DO IF LN(VAL[I]←READ)=0 THEN DONE;
		END
	    ELSE BEGIN "SET OF ARGUMENTS PER LINE"
		S←READ;
		FOR I ⊃ ARGS DO IF LN(VAL[I]←SCAN(S,MARK,BRK))=0 THEN DONE;
		END;
	IF EOF ∧ I=1 THEN DONE;
	FOR I←I THRU ARGS DO VAL[I]←NULL;
	OUT(OUCH,S0);
	FOR I ⊃ T-1 DO BEGIN OUT(OUCH,VAL[PARG[I]]); OUT(OUCH,TEXT[I]) END;
	END UNTIL EOF;
RELEASE(INCH);  RELEASE(OUCH);
IF LN(DEST) THEN SPOOL(OUCH,DEST,1);
END;